using System;
using System.Text;
using System.Security.Cryptography;
using System.Linq;
using System.Collections.Generic;
using Project362.Files;

namespace Project362
{
	public class View
    {
        public string help1;
        public string help2;
        public string help3;
        public string help4;

        public View(){
            help1 = "====Permission level 1====" + "\n" +
            "display all collections" + "\n" +
            "\t" + "Prints a list of all collections available in the system" + "\n" +
            "display all tags" + "\n" +
            "\t" + "Prints a list of all tags available in the system" + "\n" +
            "display all attributes [COLLECTION] [FILE]" + "\n" +
            "\t" + "Displays the attributes associated with file [FILE] in collection\n\t[COLLECTION]" + "\n" +
            "display collection [NAME]" + "\n" +
            "\t" + "Displays files in a collection named [NAME]" + "\n" +
            "display tag [NAME]" + "\n" +
            "\t" + "Displays files tagged with a tag named [NAME]" + "\n" +
            "display attribute [COLLECTION] [FILE] [KEY]" + "\n" +
            "\t" + "Displays the value associated with [KEY] for file [FILE] in collection\n\t[COLLECTION]" + "\n" +
            "display perms" + "\n" +
            "\t" + "Displays what permissions the current user has" + "\n" +
            "search [PREFIX] [TERM] ([OPERATOR] ([PREFIX]) [TERM] ([OPERATOR] ...))" + "\n" +
            "\t" + "Performs a search for files" + "\n" +
            "\t" + "PREFIXES (begin with a $):" + "\n" +
            "\t\t" + "$c - interpret the term as the name of a collection" + "\n" +
            "\t\t" + "$n - interpret the term as the name of a file" + "\n" +
            "\t\t" + "$t - interpret the term as the name of a tag" + "\n" +
            "\t\t" + "$m - interpret the term as a meta element, of the form\n\t\t'key=value'" + "\n" +
            "\t" + "OPERATORS: (parenthetical statements not supported)" + "\n" +
            "\t\t" + "& - returns the set intersection or AND of the preceding\n\t\tprefix/term and the following" + "\n" +
            "\t\t" + "| - returns the set union or OR of the preceding prefix/term\n\t\tand the following" + "\n" +
            "\t\t" + "~ - returns the set subtraction of the preceding prefix/term\n\t\tand the following (elements in preceding that are not in\n\t\tfollowing)" + "\n" +
            "\t\t" + "% - Sorts the preceding prefix with the following method\n\t\t(method should NOT have a prefix):" + "\n" +
            "\t\t\t" + "col - sort by collection" + "\n" +
            "\t\t\t" + "dir - sort by directory" + "\n" +
            "\t\t\t" + "name - sort by file name" + "\n" +
            "\t\t\t" + "ftype - sort by filetype" + "\n" +
            "\t\t\t" + "tags - sort by tags" + "\n" +
            "\t" + "Example search string: search for things in the collection 'foo'\n\tsorted by name, or things with tag 'bar', all sorted by directory" + "\n" +
            "\t" + "$c foo % name | $t bar % dir" + "\n" +
            "attribute [COLLECTION] [FILE] [KEY] [VALUE]" + "\n" +
            "\t" + "Adds an attribute to a file named [FILE] in a collection named\n\t[COLLECTION], where the attribute is named [KEY] and has a value\n\t[VALUE]" + "\n" +
            "update attribute [COLLECTION] [FILE] [KEY] [VALUE]" + "\n" +
            "\t" + "Updates the attribute named [KEY] on a file named [FILE] in a\n\tcollection named [COLLECTION] to the value [VALUE]" + "\n" +
            "remove attribute [COLLECTION] [FILE] [KEY]" + "\n" +
            "\t" + "Removes the attribute named [KEY] from the file named [FILE] in a\n\tcollection named [COLLECTION]" + "\n" +
            "open [COLLECTION] [NAME]" + "\n" +
            "\t" + "Opens the file named [FILE] in the collection named [COLLECTION]" + "\n" +
            "copy [COLLECTION] [DEST]" + "\n" +
            "\t" + "Copies all files in a collection named [COLLECTION] to the location\n\t[DEST]" + "\n" +
            "checksum [COLLECTION] [FILE]" + "\n" +
            "\t" + "Provides the checksum of a file named [FILE] in a collection named\n\t[COLLECTION]" + "\n" +
            "logout" + "\n" +
            "\t" + "Logs the current user out of the system and allows for another login" + "\n" +
            "exit" + "\n" +
            "\t" + "Logs the current user out of the system and quits" + "\n"
            ;
            help2 = "====Permission level 2====" + "\n" +
            "new file [NAME] [LOCATION] [COLLECTION]" + "\n" +
            "\t" + "Adds a new file located at [LOCATION] to the collection named\n\t[COLLECTION], giving it the internal name [NAME]" + "\n" +
            "remove file [COLLECTION] [FILE]" + "\n" +
            "\t" + "Removes a file named [FILE] from a collection named [COLLECTION]" + "\n" +
            "update relocate [SOURCE] [FILE] [DEST]" + "\n" +
            "\t" + "Moves a file named [FILE] from a collection named [SOURCE] to a\n\tcollection named [DEST]" + "\n" +
            "tag [COLLECTION] [FILE] [TAG]" + "\n" +
            "\t" + "Adds a tag named [TAG] to a file named [FILE] in a collection named\n\t[COLLECTION]" + "\n" +
            "untag [COLLECTION] [FILE] [TAG]" + "\n" +
            "\t" + "Removes a tag named [TAG] from a file named [FILE] in a collection\n\tnamed [COLLECTION]" + "\n" +
            "filetype [COLLECTION] [FILE] [TYPE]" + "\n" +
            "\t" + "Changes the filetype of [FILE] in [COLLECTION] to [TYPE], where [TYPE] is one of" + "\n" +
            "\t" + "DOCUMENT, IMAGE, CODE, EXECUTABLE, RESOURCE, UNSET" + "\n" +
            "convert [COLLECTION] [FILE] [TOTYPE]" + "\n" +
            "\t" + "Converts a file named [FILE] in a collection named [COLLECTION] to a\n\tfiletype named [TOTYPE]" + "\n" +
            "\t" + "Current supported values for TOTYPE are {Bmp,Jpeg,Gif}" + "\n"
            ;
            help3 = "====Permission level 3====" + "\n" +
            "new collection [NAME]" + "\n" +
            "\t" + "Creates a new collection named [NAME]" + "\n" +
            "remove collection [COLLECTION]" + "\n" +
            "\t" + "Removes a collection named [NAME] and everything in it" + "\n" +
            "new tag [NAME]" + "\n" +
            "\t" + "Adds a tag named [NAME]" + "\n" +
            "remove tag [NAME]" + "\n" +
            "\t" + "Removes a tag named [NAME]" + "\n"
            ;
            help4 = "====Permission level 4====" + "\n" +
            "display users" + "\n" +
            "\t" + "Displays a list of all users in the system" + "\n" +
            "new user [NAME] [PASSWORD] [PERMISSIONS]" + "\n" +
            "\t" + "Adds a user named [NAME] with a password [PASSWORD] and permission\n\tlevel [PERMISSIONS]" + "\n" +
            "remove user [NAME]" + "\n" +
            "\t" + "Removes a user named [NAME]" + "\n" +
            "update perms [USERNAME] [PERMISSIONS]" + "\n" +
            "\t" + "Updates a user named [USERNAME] to permission level [PERMISSIONS]" + "\n" +
            "move [COLLECTION] [NAME] [DEST]" + "\n" +
            "\t" + "Moves a file named [NAME] in a collection named [COLLECTION] to the\n\tdisk location [DEST]" + "\n" +
            "delete [COLLECTION] [NAME]" + "\n" +
            "\t" + "Removes a file named [NAME] from a collection named [COLLECTION] and\n\tdeletes it from the disk" + "\n"
            ;
            login(false);
        }
		public void login(bool exit){
            bool loggedIn;
            while (!exit)
            {
                loggedIn=false;
                string username, password;
                Console.WriteLine("Welcome to High Level File Association Structures.");
                if (Program.controller.getNumberOfUsers() == 0)
                {
                    Console.Write("This system has no users. Please enter a password for user 'admin' or type 'exit' to quit.\npassword: ");
                    password = Console.ReadLine();
                    if (password == "exit") exit = true;
                    else
                    {
                        Program.controller.AddUser("admin", password, Users.PermissionLevel.tier4);
                        Console.WriteLine("Logging you in as user admin...");
                        if (Program.controller.login("admin", password))
                        { 
                            loggedIn = true; 
                        }
                        else 
                        { 
                            Console.WriteLine("Login error, check your username and password!\n"); 
                        }
                    }
                }
                else
                {
                    Console.WriteLine("Log in, or type 'exit' to quit.");
                    Console.Write("username: ");
                    username = Console.ReadLine();
                    if (username == "exit") exit = true;
                    else
                    {
                        Console.Write("password: ");
                        password = Console.ReadLine();
                        if (password == "exit") exit = true;
                        else
                        {
                            Console.WriteLine("Logging you in as user " + username + "...");
                            if (Program.controller.login(username, password)){
                                loggedIn = true;
                            }
                            else
                            {
                                Console.WriteLine("Login error, check your username and password!\n");
                            }
                        }
                    }
                }
                if (!exit&&loggedIn)
                {
                    exit=Run();
                }
                if (exit)
                {
                    Program.controller.SaveAll();
                }
            }
		}
        public bool Run(){
            Console.WriteLine("Type 'help' for a list of commands.");
            bool session = true;
            bool exit = false;
            bool result=false;
            bool allowed = false;
            string[] command;
            while (session)
            {
                command = Console.ReadLine().ToLower().Split(' ');
                try
                {
                    allowed = false;
                    result = false;
                    if (Program.controller.getPermissionContext() == Users.PermissionLevel.tier4) allowed = true;
                    else if ((Program.controller.getPermissionContext() == Users.PermissionLevel.tier3 ||
                             Program.controller.getPermissionContext() == Users.PermissionLevel.tier2 ||
                             Program.controller.getPermissionContext() == Users.PermissionLevel.tier1)
                        && !(command[0] == "display" && command[1] == "users" ||
                            command[0] == "new" && command[1] == "user" ||
                            command[0] == "remove" && command[1] == "user" ||
                            command[0] == "update" && command[1] == "perms" ||
                            command[0] == "move" ||
                            command[0] == "update"))
                    {
                        if (Program.controller.getPermissionContext() == Users.PermissionLevel.tier3) allowed = true;
                        else if ((Program.controller.getPermissionContext() == Users.PermissionLevel.tier2 ||
                             Program.controller.getPermissionContext() == Users.PermissionLevel.tier1)
                            && !(command[0] == "new" && command[1] == "collection" ||
                                command[0] == "remove" && command[1] == "collection" ||
                                command[0] == "new" && command[1] == "tag" ||
                                command[0] == "remove" && command[1] == "tag"))
                        {
                            if (Program.controller.getPermissionContext() == Users.PermissionLevel.tier2) allowed = true;
                            else if (Program.controller.getPermissionContext() == Users.PermissionLevel.tier1
                            && !(command[0] == "new" && command[1] == "file" ||
                                command[0] == "remove" && command[1] == "file" ||
                                command[0] == "update" && command[1] == "relocate" ||
                                command[0] == "tag" ||
                                command[0] == "untag" ||
                                command[0] == "filetype" ||
                                command[0] == "convert")) allowed = true;
                        }
                    }
                    if (allowed)
                    {
                        switch (command[0])
                        {
                            case "new":
                                switch (command[1])
                                {
                                    case "collection":
                                        result = Program.controller.addNewCollection(command[2]);
                                        break;
                                    case "file":
                                        result = Program.controller.addFileToCollection(command[2], command[3], command[4]);
                                        break;
                                    case "tag":
                                        result = Program.controller.addTag(command[2]);
                                        break;
                                    case "user":
                                        result = Program.controller.AddUser(command[2], command[3], (Users.PermissionLevel)Enum.Parse(typeof(Users.PermissionLevel), command[4]));
                                        break;
                                    default:
                                        throw new ArgumentException();
                                }
                                break;
                            case "tag":
                                result = Program.controller.addTagToFile(command[1], command[2], command[3]);
                                break;
                            case "untag":
                                result = Program.controller.removeTagFromFile(command[1], command[2], command[3]);
                                break;
                            case "filetype":
                                result = Program.controller.changeFiletype(command[1], command[2], command[3]);
                                break;
                            case "attribute":
                                result = Program.controller.addAttributetoFile(command[1], command[2], command[3], command[4]);
                                break;
                            case "update":
                                switch (command[1])
                                {
                                    case "perms":
                                        result = Program.controller.ChangeUserPermissions(command[2], (Users.PermissionLevel)Enum.Parse(typeof(Users.PermissionLevel), command[4]));
                                        break;
                                    case "attribute":
                                        result = Program.controller.editAttributeContent(command[2], command[3], command[4], command[5]);
                                        break;
                                    case "relocate":
                                        result = Program.controller.moveFileBetweenCollections(command[2], command[3], command[4]);
                                        break;
                                    default:
                                        throw new ArgumentException();
                                }
                                break;
                            case "remove":
                                switch (command[1])
                                {
                                    case "attribute":
                                        result = Program.controller.removeAttributefromFile(command[2], command[3], command[4]);
                                        break;
                                    case "collection":
                                        result = Program.controller.removeCollection(command[2]);
                                        break;
                                    case "file":
                                        result = Program.controller.removeFileFromCollection(command[2], command[3]);
                                        break;
                                    case "tag":
                                        result = Program.controller.removeTag(command[2]);
                                        break;
                                    case "user":
                                        result = Program.controller.RemoveUser(command[2]);
                                        break;
                                    default:
                                        throw new ArgumentException();
                                }
                                break;
                            case "checksum":
                                Console.WriteLine(Program.controller.checksumFile(command[1], command[2]));
                                result = true;
                                break;
                            case "convert":
                                result = Program.controller.ConvertFile(command[1], command[2], command[3]);
                                break;
                            case "copy":
                                result = Program.controller.CopyFiles(command[1], command[2]);
                                break;
                            case "move":
                                result = Program.controller.MoveFile(command[1], command[2], command[3]);
                                break;
                            case "delete":
                                result = Program.controller.DeleteFile(command[1], command[2]);
                                break;
                            case "open":
                                result = Program.controller.OpenFile(command[1], command[2]);
                                break;
                            case "display":
                                switch (command[1])
                                {
                                    case "collection":
                                        foreach (Files.FileClass f in Program.controller.getFilesInCollection(command[2]))
                                        {
                                            Console.Write(f.Name + "\t" + f.Filetype.ToString()+"\t");
                                            foreach (Tags.Tag t in f.getTags())
                                            {
                                                Console.Write(t + ",");
                                            }
                                            Console.WriteLine();
                                        }
                                        result = true;
                                        break;
                                    case "tag":
                                        foreach (Files.FileClass f in Program.controller.getFilesWithTag(command[2]))
                                        {
                                            Console.WriteLine(f.Name + "\t" + f.Collection + "\t" + f.Filetype.ToString());
                                        }
                                        result = true;
                                        break;
                                    case "all":
                                        switch (command[2])
                                        {
                                            case "collections":
                                                foreach (Collections.Collection c in Program.controller.getListofallCollections())
                                                {
                                                    Console.WriteLine(c.Name);
                                                }
                                                result = true;
                                                break;
                                            case "tags":
                                                foreach (Tags.Tag t in Program.controller.getListOfTags())
                                                {
                                                    Console.WriteLine(t.Name);
                                                }
                                                result = true;
                                                break;
                                            case "attributes":
                                                foreach (string s in Program.controller.getMetas(command[3], command[4]))
                                                {
                                                    Console.WriteLine(s);
                                                }
                                                result = true;
                                                break;
                                            default:
                                                throw new ArgumentException();
                                        }
                                        break;
                                    case "users":
                                        Console.WriteLine(Program.controller.getNumberOfUsers());
                                        result = true;
                                        break;
                                    case "perms":
                                        Console.WriteLine("You currently have permission level " + Program.controller.getPermissionContext().ToString() + ".");
                                        result = true;
                                        break;
                                    case "attribute":
                                        Console.WriteLine(command[4] + ": " + Program.controller.getMetadataContents(command[2], command[3], command[4]));
                                        result = true;
                                        break;
                                    default:
                                        throw new ArgumentException();
                                }
                                break;
                            case "logout":
                                session = false;
                                result = true;
                                break;
                            case "exit":
                                session = false;
                                exit = true;
                                result = true;
                                break;
                            case "help":
                                Console.WriteLine("AVAILABLE COMMANDS:");
                                switch (Program.controller.getPermissionContext())
                                {
                                    case Users.PermissionLevel.tier4:
                                        Console.WriteLine(help4);
                                        goto case Users.PermissionLevel.tier3; //case fallthrough not allowed in C#
                                    case Users.PermissionLevel.tier3:
                                        Console.WriteLine(help3);
                                        goto case Users.PermissionLevel.tier2;
                                    case Users.PermissionLevel.tier2:
                                        Console.WriteLine(help2);
                                        goto case Users.PermissionLevel.tier1;
                                    case Users.PermissionLevel.tier1:
                                        Console.WriteLine(help1);
                                        break;
                                }
                                result = true;
                                break;
                            case "search":
                                int i=1;
                                string prefixl=null;
                                string prefixr=null;
                                string terml=null;
                                string termr=null;
                                string oper=null;
                                string[] opers={"&","|","~","%"};
                                List<FileClass> partial=null;
                                while (i < command.Length)
                                {
                                    if (command[i][0] == '$')
                                    {
                                        if (prefixl == null) prefixl = command[i];
                                        else if (prefixr == null) prefixr = command[i];
                                        else throw new ArgumentException();
                                    }
                                    else if (command[i].Length == 1 && opers.Contains(command[i]))
                                    {
                                        if (oper == null) oper = command[i];
                                        else throw new ArgumentException();
                                    }
                                    else
                                    {
                                        if (terml == null) terml = command[i];
                                        else if (termr == null) termr = command[i];
                                        else throw new ArgumentException();
                                    }
                                    if (terml != null && partial == null)
                                    {
                                        if (terml != null)
                                        {
                                            switch (prefixl[1])
                                            {
                                                case 't':
                                                    partial=Program.controller.searchForTag(terml);
                                                    break;
                                                case 'n':
                                                    partial = Program.controller.searchForFilename(terml);
                                                    break;
                                                case 'm':
                                                    partial = Program.controller.searchForMeta(terml.Split('=')[0], terml.Split('=')[1]);
                                                    break;
                                                case 'c':
                                                    partial = Program.controller.searchForCollection(terml);
                                                    break;
                                            }
                                        }
                                        terml=null;
                                        prefixl=null;
                                    }
                                    if(termr!=null&&oper!=null){
                                        List<FileClass> right=null;
                                        if (oper == "%" && prefixr != null) throw new ArgumentException();
                                        if (oper != "%")
                                        {
                                            switch (prefixr[1])
                                            {
                                                case 't':
                                                    right = Program.controller.searchForTag(termr);
                                                    break;
                                                case 'n':
                                                    right = Program.controller.searchForFilename(termr);
                                                    break;
                                                case 'm':
                                                    right = Program.controller.searchForMeta(terml.Split('=')[0], terml.Split('=')[1]);
                                                    break;
                                                case 'c':
                                                    right = Program.controller.searchForCollection(termr);
                                                    break;
                                            }
                                        }
                                        prefixr = null;
                                        switch (oper)
                                        {
                                            case "&":
                                                partial = Program.controller.booleanAnd(partial, right);
                                                break;
                                            case "|":
                                                partial = Program.controller.booleanOr(partial, right);
                                                break;
                                            case "~":
                                                partial = Program.controller.booleanNot(partial, right);
                                                break;
                                            case "%":
                                                switch (termr)
                                                {
                                                    case "col":
                                                        partial = Program.controller.sortByCollection(partial);
                                                        break;
                                                    case "dir":
                                                        partial = Program.controller.sortByDirectory(partial);
                                                        break;
                                                    case "ftype":
                                                        partial = Program.controller.sortByFiletype(partial);
                                                        break;
                                                    case "name":
                                                        partial = Program.controller.sortByName(partial);
                                                        break;
                                                    case "tags":
                                                        partial = Program.controller.sortByTags(partial);
                                                        break;
                                                    default:
                                                        throw new ArgumentException();
                                                }
                                                break;
                                        }
                                        termr = null;
                                        oper = null;
                                    }
                                    i++;
                                    result = true;
                                }
                                foreach (FileClass f in partial)
                                {
                                    Console.WriteLine(f.Name + "\t" + f.Collection + "\t" + f.Filetype.ToString());
                                }
                                break;
                            default:
                                throw new ArgumentException();
                        }
                    }
                    else
                    {
                        Console.WriteLine("You do not have permission to execute that command.");
                    }
                }
                catch (System.IndexOutOfRangeException)
                {
                    Console.WriteLine("Missing argument; please check your command or type 'help'.");
                }
                catch (ArgumentException)
                {
                    Console.WriteLine("Invalid argument; please check your command or type 'help'.");
                }
                catch (System.Exception e)
                {
                    Console.WriteLine("Command Error: " + e.GetType());
                }
                if (result) Console.WriteLine("[OK]");
                else Console.WriteLine("[FAIL]");
            }
            Program.controller.logout();
            return exit;
            /*
            if (new Users.PermissionLevel[]{Users.PermissionLevel.tier1,
                Users.PermissionLevel.tier2,
                Users.PermissionLevel.tier3,
                Users.PermissionLevel.tier4}.Contains(p))
            {
                Program.controller
            }
            if (new Users.PermissionLevel[]{
                Users.PermissionLevel.tier2,
                Users.PermissionLevel.tier3,
                Users.PermissionLevel.tier4}.Contains(p))
            {
                Program.controller.addAttributetoFile
            }
            if (new Users.PermissionLevel[]{
                Users.PermissionLevel.tier3,
                Users.PermissionLevel.tier4}.Contains(p))
            {
                //functions available to 3+
            }
            if (new Users.PermissionLevel[]{
                Users.PermissionLevel.tier4}.Contains(p))
            {
                //functions available to 4+
            }
             * */
        }
	}
}

